package com.ys.utils.spring.filter;

import com.aliyun.openservices.shade.com.alibaba.fastjson.JSONObject;
import com.ys.utils.base.http.McodeHandler;
import com.ys.utils.base.http.RequestHandler;
import com.ys.utils.base.http.ResponseHandler;
import com.ys.utils.base.j2se.DateHandler;
import com.ys.utils.base.j2se.Logger;
import com.ys.utils.base.j2se.StringHandler;
import com.ys.utils.redis.Redis;
import com.ys.utils.redis.key.UserKey;
import com.ys.utils.spring.ServerUrl;
import com.ys.utils.spring.config.SystemError;
import com.ys.utils.spring.properties.Config;
import com.ys.utils.spring.properties.SysConfig;
import com.ys.web.config.manager.UrlManager;
import org.nutz.json.Json;
import org.nutz.json.JsonFormat;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;

/**
 * @program: ys1->ServerFilter
 * @Auther: 但何非
 * @Date: 2022/5/30 09:10
 * @Description:
 */
@WebFilter(filterName = "serverFilter",urlPatterns = "/*")
public class ServerFilter implements Filter {
    private String proName;
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    //项目名称
        this.proName = filterConfig.getServletContext().getContextPath();
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        long begin = System.currentTimeMillis();
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        request.setCharacterEncoding("utf-8");
        request.setAttribute("system.time", System.currentTimeMillis());
        String uri = request.getRequestURI();
        if(uri.contains("index.html")){
            ResponseHandler.write(ResponseHandler.error("500", "非法路径--index.html"), request, response);
            return;
        }
        if(uri.contains("favicon.ico")){
            ResponseHandler.write(ResponseHandler.error("500", "非法路径--favicon.ico"), request, response);
            return;
        }

        String ip = RequestHandler.getIP(request);
        if(Config.pp.get("sys.black.ips").contains(ip)){
            String alias = request.getParameter("alias");
            String token = request.getParameter("token");
            Logger.error(String.format("用户登ip被封,IP：%s,路径：%s,alias：%s,token：%s",ip,uri,alias,token));
            ResponseHandler.write(ResponseHandler.error("500", "系统繁忙..."), request, response);
            return;
        }
        //限制请求内容大小
        String rpid = UUID.randomUUID().toString();//生成请求编号
        if (request.getContentLength() > 1048576) {
            String error = "请求内容大小超过限制"+(1048576/1024)+"KB";
            Logger.info("%s [server]receive:[%s] %s", rpid, uri, error);
            ResponseHandler.write(ResponseHandler.error("6002", error), request, response);
            return;
        }
        String content = "";
        ServerUrl serverUrl = null;
        String json = "";
        try{
//            ipAllLimit(uri,ip);
            //各种认证
            serverUrl = auth(uri, request, response,ip);
            if(StringHandler.isNotEmpty(serverUrl) && "json".equals(serverUrl.forwardParamsFormat)){
                Map<String, String> params = RequestHandler.params(request);
                if(StringHandler.isEmpty(params)){
                    request.setAttribute("json", "");
                }else{
                    json = Json.toJson(params, JsonFormat.compact());
                    params.clear();
                    request.setAttribute("json", json);
                }

            }
            //模拟接口
            if(StringHandler.isNotEmpty(serverUrl) && serverUrl.ismock){
                ResponseHandler.write(StringHandler.getString(serverUrl.mockResult), request, response);
                Logger.info("请求信息   Url：[" + uri +"] ip ["+ip+"] 参数:  " + JSONObject.toJSONString(this.getaramsMaps(request.getParameterMap())) +" 请求结果  时间 " + (System.currentTimeMillis()-begin) + "毫秒,返回参数:  " + StringHandler.getString(serverUrl.mockResult));
                return;
            }
            //注入IP
            request.setAttribute("ip", ip);
            ResponseWrapper responseWrapper = new ResponseWrapper(response);
            filterChain.doFilter(request, responseWrapper);
            // 返回的结果
            content = responseWrapper.getTextContent();
//            response.getOutputStream().write(content.getBytes());
            ResponseHandler.write(content, request, response);
        } catch (Exception e) {
            SystemError error = SystemError.exp2Se(e);
            if(!error.getErrorCode().equals("6007") && !error.getErrorCode().equals("6008")){
                Logger.error(uri, e);
            }
            ResponseHandler.write(ResponseHandler.error(error.getErrorCode(), error.getMessage()), request, response);
            content = error.getMessage();
        }
        if(StringHandler.isNotEmpty(serverUrl) && "json".equals(serverUrl.forwardParamsFormat)){
            Logger.info("请求信息   Url：[" + uri +"] ip ["+ip+"] 参数:  " + json +" 请求结果  时间 " + (System.currentTimeMillis()-begin) + "毫秒,返回参数:  " + content);
        }else{
            Logger.info("请求信息   Url：[" + uri +"] ip ["+ip+"] 参数:  " + JSONObject.toJSONString(this.getaramsMaps(request.getParameterMap())) +" 请求结果  时间 " + (System.currentTimeMillis()-begin) + "毫秒,返回参数:  " + content);
        }
    }

    @Override
    public void destroy() {

    }

    public Map<String, Object> getaramsMaps(Map<String, String[]> params ) {
        Map<String, Object> paramsMaps = new HashMap<>();
        if(StringHandler.isEmpty(params) || params.size()<=0)
            return paramsMaps;
        Iterator<Map.Entry<String, String[]>> iterator =  params.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String[]> next = iterator.next();
            paramsMaps.put(next.getKey(), next.getValue()[0]);
        }
        return paramsMaps;
    }
    /**
     * 各种限制
     * 签名 + referer + 请求次数
     */
    private ServerUrl auth(String uri, HttpServletRequest request, HttpServletResponse response,String ip){
        //请求认证
        ServerUrl url = UrlManager.url(uri);
        if(null == url || url.loginAuth){
            String alias = request.getParameter("alias");
            String token = request.getParameter("token");
            String uid = Redis.str.get(UserKey.uidByToken(token));
            if(StringHandler.isEmpty(uid) || McodeHandler.from32Radix(alias)!=StringHandler.getLong(uid)){
                Logger.error(String.format("用户登录失败,IP：%s,路径：%s,alias：%s,token：%s",ip,uri,alias,token));
                SystemError.wrapBs("6007", Config.pp.get("6007"));
            }

            request.setAttribute("uid", StringHandler.getLong(uid));
            //用户次数限制
            if(StringHandler.getLong(uid) > 0 && StringHandler.isNotEmpty(url) && url.userLimit){
                long min = DateHandler.getLongFormatDate(System.currentTimeMillis())/100000;
                Long limit = Redis.hash.hincrBy("AuthLimit_"+min,uid+"."+uri, 1,60);
                if(null != limit && limit > url.userCount){
                    if(!Config.pp.get("sys.black.ips").contains(ip)) {
                        Config.pp.put("sys.black.ips", Config.pp.get("sys.black.ips") + "," + ip);
                        SysConfig.addBlackIp("," + ip);
                        Logger.error(String.format("频繁请求,IP:%s,路径：%s,用户：%s", ip, uri, uid));
                    }
                    SystemError.wrapBs("6008", "频繁操作，请稍后再试");

                }
            }
            return url;
        }else{
            if(StringHandler.isEmpty(url)){
                return new ServerUrl();
            }else{
                return url;
            }
        }
    }

    private void ipAllLimit(String uri, String ip){
        long min = DateHandler.getLongFormatDate(System.currentTimeMillis())/100000;
        Long limit = Redis.hash.hincrBy("AuthLimit_"+min,ip.replaceAll("\\.",""), 1,60);
        if(null != limit && limit > StringHandler.getInt(Config.pp.get("sys.ip.all.limit"))){
//            Config.pp.put("sys.black.ips",Config.pp.get("sys.black.ips")+","+ip);
//            SysConfig.addBlackIp(","+ip);
            Logger.error(String.format("频繁请求,路径：%s,ip：%s",uri,ip));
//            SystemError.wrapBs("6008", "频繁操作，请稍后再试..");
        }
    }
}
